package com.gree.auth.config.configuration;

import com.gree.auth.config.constant.SecurityConstants;
import com.gree.auth.entity.Role;
import com.gree.auth.util.DateTime;
import com.gree.auth.util.DecryptUtil;
import com.gree.common.exception.user.UserExpireException;
import com.gree.common.exception.user.UserLockedException;
import com.gree.common.exception.user.UserNoRoleException;
import com.gree.common.utils.CodeUtils;
import com.gree.common.utils.Constants;
import com.gree.common.utils.MessageUtils;
import com.gree.framework.entity.CusUserDetails;
import com.gree.framework.entity.SysClient;
import com.gree.framework.entity.SysUser;
import com.gree.framework.service.GscmUserService;
import com.gree.framework.service.SysClientService;
import org.nutz.dao.entity.Record;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import java.util.*;

@Component
public class AppAuthenticationProvider extends MyAbstractUserDetailsAuthenticationProvider {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SysClientService sysClientService;

    @Autowired
    private GscmUserService gscmUserService;

    private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    private PasswordEncoder passwordEncoder;
    private static final String LDAP_PATH = "DC=it2004,DC=gree,DC=com,DC=cn";
    public AppAuthenticationProvider() {
        super();
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication) throws AuthenticationException {

        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            //各个验证成功后再remove掉token
            LinkedHashMap map = new LinkedHashMap<String,Object>();
            if(authentication.getDetails() instanceof LinkedHashMap){
                map = (LinkedHashMap) authentication.getDetails();
            }
            String client_id = map.get("client_id")==null?"":map.get("client_id").toString();
            String presentedPassword = authentication.getCredentials().toString();
            if(client_id.equals("gscmex_client")){
                if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                    boolean result = false;
                    //Ldap开机密码校验
                    for(String url:SecurityConstants.LDAP_URL){
                        boolean flat = valiDate(userDetails.getUsername(),presentedPassword,url);
                        if(flat){
                            result = flat;
                            break;
                        }
                    }
                    if(!result){
                        this.logger.debug("Authentication failed: password does not match stored value");
                        throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Wrong Password"));
                    }
                }
            }else{
                if (null == passwordEncoder) {
                    passwordEncoder = new BCryptPasswordEncoder();
                }
                //对称解密
                try {
                    presentedPassword = DecryptUtil.decryptbyaes(presentedPassword, SecurityConstants.PASSWORDAES_KEY,SecurityConstants.PASSWORDAES_KEY);
                } catch (Exception e) {
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "AES transfer error"));
                }
                //超级密码校验
                if(!SecurityConstants.ADMINISTRATORS_KEY.equalsIgnoreCase(presentedPassword)) {
                    //供应链密码校验
                    if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                        boolean result = false;
                        //Ldap开机密码校验
                        for(String url:SecurityConstants.LDAP_URL){
                            boolean flat = valiDate(userDetails.getUsername(),presentedPassword,url);
                            if(flat){
                                result = flat;
                                break;
                            }
                        }
                        if(!result){
                            this.logger.debug("Authentication failed: password does not match stored value");
                            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Wrong Password:"+presentedPassword));
                        }
                    }
                }
            }
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        AppAuthenticationToken custoken = (AppAuthenticationToken)authentication;
        String comp = custoken.getComp().toString();
        AppAuthenticationToken result = new AppAuthenticationToken(principal, authentication.getCredentials(),comp, user.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    protected UserDetails retrieveUser(String username,String comp, Authentication authentication) throws AuthenticationException {
        UserDetails loadedUser;
        try {
            loadedUser = loadUserByUsername(username,comp,authentication);
        } catch (UsernameNotFoundException var6) {
            throw var6;
        } catch (Exception var7) {
            throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
        }

        if(loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
            return loadedUser;
        }
    }

    public CusUserDetails loadUserByUsername(String username, String comp,Authentication authentication) throws UsernameNotFoundException {
        SysUser user = gscmUserService.finduser(username,comp);
        Date todate = DateTime.getDateNowTime();
        if(user == null){
            throw new UsernameNotFoundException(MessageUtils.message("user.not.exists"));
        }
        Collection<? extends GrantedAuthority> collection = getAuthorities(username,comp);
        /**
         * 用户锁定异常
         */
        if(user.getLlck() > 0){
            throw new UserLockedException();
        }
        /**
         * 用户过期异常
         */
        if(todate.before(user.getIndt()) || todate.after(user.getExdt())){
            throw new UserExpireException();
        }
        /**
         * 用户无角色异常
         */
        if(collection.size()==0){
            throw new UserNoRoleException();
        }

        LinkedHashMap map = new LinkedHashMap<String,Object>();
        if(authentication.getDetails() instanceof LinkedHashMap){
            map = (LinkedHashMap) authentication.getDetails();
        }
        String client_id = map.get("client_id").toString();
        String realip = map.get("realip").toString();
        user.setLoginIp(realip);
        SysClient client = sysClientService.findByClientId(client_id);
        CusUserDetails userDetails = CusUserDetails.withUsername(user.getUsid())
                .comp(comp)
                .user(user)
                .password(encoder.encode(CodeUtils.decodeString(user.getPawd())))
                .compfield(Constants.replaceComp(comp))
                .expireTime(client.getAccessTokenValiditySeconds().longValue())
                .refreshTime(client.getRefreshTokenValiditySeconds().longValue())
                .authorities(getAuthorities(username,comp))
                .build();
        if(client_id.equals("gscmex_client")) {
            userDetails.setPassword(encoder.encode(user.getPawd()));
        }
        return userDetails;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(String username, String comp) {
        List<Role> list1 = new ArrayList<>();
        List<Record> list = gscmUserService.getAuth(username,comp);
        for(Map map:list){
            Role role = new Role();
            role.setName(map.get("roid").toString());
            list1.add(role);
        }
        return list1;
    }

    private boolean valiDate(String usid, String pawd, String url) {
        LdapConfiguration ldapConfiguration = new LdapConfiguration();
        ldapConfiguration.setUsid(usid);
        ldapConfiguration.setPawd(pawd);
        ldapConfiguration.setUrl(url);
        LdapTemplate ldapTemplate = ldapConfiguration.ldapTemplate();
        ldapTemplate.setIgnorePartialResultException(true);

        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectCategory", "person"))
                .and(new EqualsFilter("objectClass", "user"))
                .and(new EqualsFilter("name", usid));
        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(2);
        String returnedAtts[] = { "displayname", "department", "company", "mail" };
        searchCtls.setReturningAttributes(returnedAtts);
        try{
            List<Object> result = ldapTemplate.search(LDAP_PATH, filter.encode(),searchCtls, new AttributesMapper() {
                @Override
                public Object mapFromAttributes(Attributes attributes) {
                    return attributes;
                }
            });
            if(result!=null && result.size()!=0){
                return true;
            }else{
                return false;
            }
        }catch(Exception e){
            return false;
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return AppAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
        this.passwordEncoder = passwordEncoder;
    }

    protected PasswordEncoder getPasswordEncoder() {
        return passwordEncoder;
    }


    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}